#include "dev_sign_api.h"
#include "mqtt_api.h"
#include "cJSON.h"
#include "hwled.h"
#include "hw18b20.h"
#include "hwbeep.h"


//官方三元组
char DEMO_PRODUCT_KEY[IOTX_PRODUCT_KEY_LEN + 1] = {0};
char DEMO_DEVICE_NAME[IOTX_DEVICE_NAME_LEN + 1] = {0};
char DEMO_DEVICE_SECRET[IOTX_DEVICE_SECRET_LEN + 1] = {0};

void *HAL_Malloc(uint32_t size);
void HAL_Free(void *ptr);
void HAL_Printf(const char *fmt, ...);
int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN + 1]);
int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN + 1]);
int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN]);
uint64_t HAL_UptimeMs(void);
int HAL_Snprintf(char *str, const int len, const char *fmt, ...);

#define EXAMPLE_TRACE(fmt, ...)  \
    do { \
        HAL_Printf("%s|%03d :: ", __func__, __LINE__); \
        HAL_Printf(fmt, ##__VA_ARGS__); \
        HAL_Printf("%s", "\r\n"); \
    } while(0)

#define FOOD_ROOM_LED	0
#define BED_ROOM_LED	1
#define LIVING_ROOM_LED	2
#define WC_ROOM_LED		3

#define DATA_TYPE_INT		0x1201
#define DATA_TYPE_FLOAT		0x1202
#define DATA_TYPE_STRING	0x1203

#define LED_BEDROOM_KEY		("x6818_bed_led")
#define LED_FOODROOM_KEY	("x6818_food_led")
#define LED_LIVINGROOM_KEY	("x6818_livingroom_led")
#define LED_WCROOM_KEY		("x6818_wc_led")
#define TEMP_18B20_KEY		("x6818_temp")
#define BEEP_KEY			("x6818_beep")


static led_t foodroom_led;
static led_t bedroom_led;
static led_t livingroom_led;
static led_t wcroom_led;
static void* pclient = NULL;
   
extern int example_publish(void *handle, char* msgkey, void* msg, int dataType);

/**
 *初始化温度设备
 */ 
static void init_devTemprature(void){
	temp_init();
};

/**
 * 从底层获取温度信息
 */ 
static float get_devTemprature(void){
	return temp_getval();
};

/**
 *释放温度设备资源
 */ 
static void free_devTemprature(void){
	temp_close();
};


/**
 * 初始化led设备
 */ 
static void init_devLed(void){
	led_init();
};

/**
 *释放led设备资源
 */ 
static void free_devLed(void){
	led_free();
};

/**
 *获取led状态
 */ 
static void get_devLed_state(int ledtype, led_t* led){
	led_staut(ledtype, led);
};

/**
 *开灯
 */ 
static void on_devLed(int ledtype){
	led_on(ledtype);
};

/**
 *关灯
 */ 
static void off_devLed(int ledtype){
	led_off(ledtype);
};
 
/**
 *初始化beep蜂鸣器
 */ 

static void init_beep(void){
	beep_init();
};

/**
 *释放beep资源
 */ 
static void free_beep(void){
	beep_exit();
};

/**
 * 打开蜂鸣器
 */ 
static void on_beep(void){
	beep_on();
};

/**
 *关闭蜂鸣器
 */ 
static void off_beep(void){
	beep_off();
};

static char* publish_key(int ledindex){
	
	if(ledindex == FOOD_ROOM_LED){
		return LED_FOODROOM_KEY; 
	} else if(ledindex == BED_ROOM_LED){
		return LED_BEDROOM_KEY;
	} else if(ledindex == LIVING_ROOM_LED){
		return LED_LIVINGROOM_KEY;
	} else {
		return LED_WCROOM_KEY;
	}
}


static void ctrl_beep(int ctl) {
	int state;
	if(ctl){
		on_beep();
	} else {
		off_beep();
	}

	beep_state(&state);
	
	if(pclient){
		example_publish(pclient, BEEP_KEY, &state, DATA_TYPE_INT); 
	}

};

static void ctrl_led(int ctl, int ledindex){
	
	if(ctl){
		off_devLed(ledindex);
	} else {
		on_devLed(ledindex);
	}


	led_t *pled= NULL;
	
	switch(ledindex) {
		case FOOD_ROOM_LED:
			pled = &foodroom_led;
			break;
		case BED_ROOM_LED:
			pled = &bedroom_led;
			break;
		case LIVING_ROOM_LED:
			pled = &livingroom_led;
			break;
		case WC_ROOM_LED:
			pled = &wcroom_led;
			break;
	}

	get_devLed_state(ledindex, pled);
	
	int ledval = pled->stauts;
			
	EXAMPLE_TRACE("%d LED ROOM STAUTS = %d # %s\n", ledindex, ledval, ledval?"关闭":"打开");

	if(pclient){
		example_publish(pclient, publish_key(ledindex), &ledval, DATA_TYPE_INT); 
	}

};


/**
 * 获取云端下发的信息
 */ 
void example_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
	
	cJSON *json = NULL;
	cJSON *params = NULL;
	cJSON *bedled = NULL;
	cJSON *wcled = NULL;
	cJSON *foodled = NULL;
	cJSON *livingled = NULL;
	cJSON *beep = NULL;

	iotx_mqtt_topic_info_t     *topic_info = (iotx_mqtt_topic_info_pt) msg->msg;

    switch (msg->event_type) {
        case IOTX_MQTT_EVENT_PUBLISH_RECEIVED:
            /* print topic name and topic message */
            EXAMPLE_TRACE("Message Arrived:");
            EXAMPLE_TRACE("Topic  : %.*s", topic_info->topic_len, topic_info->ptopic);
            EXAMPLE_TRACE("Payload: %.*s", topic_info->payload_len, topic_info->payload);
			EXAMPLE_TRACE("\n");

			json = cJSON_Parse(topic_info->payload);

			if(!json){
				
				EXAMPLE_TRACE("%s: json is null\n", __func__);
			
				return;
			} 

		
			params = cJSON_GetObjectItem(json, "params");
			

			if(!params) {
				
				EXAMPLE_TRACE("%s: json  params is null\n", __func__);
		
				return;
			}

			bedled = cJSON_GetObjectItem(params, "x6818_bed_led");
	
			if(bedled && bedled->type == cJSON_Number) {
			
				ctrl_led(bedled->valueint, BED_ROOM_LED);
			
			}

			wcled = cJSON_GetObjectItem(params, "x6818_wc_led");
	
			if(wcled && wcled->type == cJSON_Number){
				ctrl_led(wcled->valueint, WC_ROOM_LED);
			

			}

			foodled = cJSON_GetObjectItem(params, "x6818_food_led");
	
			if(foodled && foodled->type == cJSON_Number){
				ctrl_led(foodled->valueint, FOOD_ROOM_LED);
			

			}

			livingled = cJSON_GetObjectItem(params, "x6818_livingroom_led");

			if(livingled && livingled->type == cJSON_Number){
				ctrl_led(livingled->valueint, LIVING_ROOM_LED);
			}

			beep = cJSON_GetObjectItem(params, "x6818_beep");
			
			if(beep && beep->type ==cJSON_Number){
				ctrl_beep(beep->valueint);
			}

			cJSON_Delete(json);
            break;
        default:
            break;
    }
}

/**
 *订阅云端向LED设备端下发指令主题
 */ 
int subscribe_led_cmd(void *handle)
{
	
	//topic： /sys/a1fsnvyGLTu/ihome_app/thing/service/property/set
	
    int res = 0;
    const char *fmt = "/sys/%s/%s/thing/service/property/set";
    char *topic = NULL;
    int topic_len = 0;

    topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    topic = HAL_Malloc(topic_len);
    if (topic == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(topic, 0, topic_len);
    HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, example_message_arrive, NULL);
    if (res < 0) {
        EXAMPLE_TRACE("subscribe failed");
        HAL_Free(topic);
        return -1;
    }


	HAL_Free(topic);
    
	return 0;
}

	

/**
 *向云端上报数据
 */ 
int example_publish(void *handle, char* msgkey, void* msg, int dataType)
{

//publish topic=/sys/a1fsnvyGLTu/ihome_app/thing/event/property/post, payload={"id":1740020739503,"params":{"x6818_bed_led":1},"version":"1.0","method":"thing.event.property.post"}
//publish topic=/sys/a1fsnvyGLTu/ihome_app/thing/event/property/post, payload={"id":1740020739503,"params":{"x6818_temp":26},"version":"1.0","method":"thing.event.property.post"}

    int             res = 0;
    const char     *fmt = "/sys/%s/%s/thing/event/property/post";
    char           *topic = NULL;
    int             topic_len = 0;
	char		    payload[400];

	if (dataType == DATA_TYPE_INT) {

			EXAMPLE_TRACE("dataType is INT\n");
			char* bint = "{\"params\":{\"%s\":%d},\"version\":\"1.0\",\"method\":\"thing.event.property.post\"}";
			int *pidata = (int*)msg;
			
			EXAMPLE_TRACE("data is %d\n", *pidata);
			EXAMPLE_TRACE("key is %s\n", msgkey);

			sprintf(payload, bint, msgkey, *pidata);
			EXAMPLE_TRACE("dataType is INT; playload = %s\n", payload);
			
	} else if(dataType == DATA_TYPE_FLOAT){
			
			char* bfloat = "{\"params\":{\"%s\":%.2f},\"version\":\"1.0\",\"method\":\"thing.event.property.post\"}";
			float *pfdata = (float*)msg;
			sprintf(payload, bfloat, msgkey, *pfdata);
		    EXAMPLE_TRACE("dataType is FLOAT; playload = %s\n", payload);
		
	} else if(dataType ==  DATA_TYPE_STRING){
			
			char* bstring = "{\"params\":{\"%s\":\"%s\"},\"version\":\"1.0\",\"method\":\"thing.event.property.post\"}";
			char* psdata = (char*)msg;
			sprintf(payload, bstring, msgkey, *psdata);
			EXAMPLE_TRACE("dataType is STRING; playload = %s\n", payload);
	}

    topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    topic = HAL_Malloc(topic_len);
    if (topic == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(topic, 0, topic_len);
    HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload));
    if (res < 0) {
        EXAMPLE_TRACE("publish failed, res = %d", res);
        HAL_Free(topic);
        return -1;
    }

    HAL_Free(topic);
    return 0;
}

void example_event_handle(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
    EXAMPLE_TRACE("msg->event_type : %d", msg->event_type);
}


/*
 *  NOTE: About demo topic of /${productKey}/${deviceName}/user/get
 *
 *  The demo device has been configured in IoT console (https://iot.console.aliyun.com)
 *  so that its /${productKey}/${deviceName}/user/get can both be subscribed and published
 *
 *  We design this to completely demonstrate publish & subscribe process, in this way
 *  MQTT client can receive original packet sent by itself
 *
 *  For new devices created by yourself, pub/sub privilege also requires being granted
 *  to its /${productKey}/${deviceName}/user/get for successfully running whole example
 */

int main(int argc, char *argv[])
{
    //void                   *pclient = NULL;
    
	int                     res = 0;
    int                     loop_cnt = 0;
    iotx_mqtt_param_t       mqtt_params;


	//初始化led设备
	init_devLed();
	//初始化温度设备
	init_devTemprature();
	//初始化beep蜂鸣器
	init_beep();


    HAL_GetProductKey(DEMO_PRODUCT_KEY);
    HAL_GetDeviceName(DEMO_DEVICE_NAME);
    HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);

    EXAMPLE_TRACE("mqtt example");

    /* Initialize MQTT parameter */
    /*
     * Note:
     *
     * If you did NOT set value for members of mqtt_params, SDK will use their default values
     * If you wish to customize some parameter, just un-comment value assigning expressions below
     *
     **/
    memset(&mqtt_params, 0x0, sizeof(mqtt_params));

    /**
     *
     *  MQTT connect hostname string
     *
     *  MQTT server's hostname can be customized here
     *
     *  default value is ${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com
     */
    /* mqtt_params.host = "something.iot-as-mqtt.cn-shanghai.aliyuncs.com"; */

    /**
     *
     *  MQTT connect port number
     *
     *  TCP/TLS port which can be 443 or 1883 or 80 or etc, you can customize it here
     *
     *  default value is 1883 in TCP case, and 443 in TLS case
     */
    /* mqtt_params.port = 1883; */

    /**
     *
     * MQTT request timeout interval
     *
     * MQTT message request timeout for waiting ACK in MQTT Protocol
     *
     * default value is 2000ms.
     */
    /* mqtt_params.request_timeout_ms = 2000; */

    /**
     *
     * MQTT clean session flag
     *
     * If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from
     * the current Session (as identified by the Client identifier).
     *
     * If CleanSession is set to 1, the Client and Server MUST discard any previous Session and Start a new one.
     *
     * default value is 0.
     */
    /* mqtt_params.clean_session = 0; */

    /**
     *
     * MQTT keepAlive interval
     *
     * KeepAlive is the maximum time interval that is permitted to elapse between the point at which
     * the Client finishes transmitting one Control Packet and the point it starts sending the next.
     *
     * default value is 60000.
     */
    /* mqtt_params.keepalive_interval_ms = 60000; */

    /**
     *
     * MQTT write buffer size
     *
     * Write buffer is allocated to place upstream MQTT messages, MQTT client will be limitted
     * to send packet no longer than this to Cloud
     *
     * default value is 1024.
     *
     */
    /* mqtt_params.write_buf_size = 1024; */

    /**
     *
     * MQTT read buffer size
     *
     * Write buffer is allocated to place downstream MQTT messages, MQTT client will be limitted
     * to recv packet no longer than this from Cloud
     *
     * default value is 1024.
     *
     */
    /* mqtt_params.read_buf_size = 1024; */

    /**
     *
     * MQTT event callback function
     *
     * Event callback function will be called by SDK when it want to notify user what is happening inside itself
     *
     * default value is NULL, which means PUB/SUB event won't be exposed.
     *
     */
    mqtt_params.handle_event.h_fp = example_event_handle;

	//建立连接
    pclient = IOT_MQTT_Construct(&mqtt_params);
    if (NULL == pclient) {
        EXAMPLE_TRACE("MQTT construct failed");
        return -1;
    }

    res = subscribe_led_cmd(pclient);

    if (res < 0) {
        IOT_MQTT_Destroy(&pclient);
        return -1;
    }

	//res = subscrib_ledstate_push(pclient);

	//if(res < 0) {
     //   IOT_MQTT_Destroy(&pclient);
    //    return -1;
	//}


    while (1) {
        if (0 == loop_cnt % 20) {
	
			//get_devLed_state(FOOD_ROOM_LED, &bedroom_led);
			//get_devLed_state(BED_ROOM_LED, &foodroom_led);
			//get_devLed_state(LIVING_ROOM_LED, &livingroom_led);
			//get_devLed_state(WC_ROOM_LED, &wcroom_led);

			//int ledbedval = bedroom_led.stauts;
			//int ledfoodval =foodroom_led.stauts;
			//int ledlivingval = livingroom_led.stauts;
			//int ledwcval = wcroom_led.stauts;

			float temp = get_devTemprature();

			//EXAMPLE_TRACE("LED BED ROOM STAUTS = %d # %s\n", ledbedval, ledbedval?"关闭":"打开");
			//EXAMPLE_TRACE("LED FOOD ROOM STAUTS = %d # %s\n", ledfoodval, ledfoodval?"关闭":"打开");
			//EXAMPLE_TRACE("LED LIVING ROOM STAUTS = %d # %s\n", ledlivingval, ledlivingval?"关闭":"打开");
			//EXAMPLE_TRACE("LED WC ROOM STAUTS = %d # %s\n", ledwcval, ledwcval?"关闭":"打开");
		
			EXAMPLE_TRACE("TEMPRATURE  = %.2f\n", temp);


//			example_publish(pclient, LED_BEDROOM_KEY, &ledbedval, DATA_TYPE_INT);
//			example_publish(pclient, LED_FOODROOM_KEY, &ledfoodval, DATA_TYPE_INT);
//          example_publish(pclient, LED_LIVINGROOM_KEY, &ledlivingval, DATA_TYPE_INT);
//			example_publish(pclient, LED_WCROOM_KEY, &ledwcval, DATA_TYPE_INT);
           
			example_publish(pclient, TEMP_18B20_KEY, &temp, DATA_TYPE_FLOAT);
        }

        IOT_MQTT_Yield(pclient, 400);

        loop_cnt += 1;
    }

    return 0;
}
